#include "cjpeg_img.h"

// #include "cdjpeg.h"
#include "cdjpeg.h"


// #include "jversion.h"
// #include "jconfigint.h"
// #include "rdpng.c"



#define JMESSAGE(code, string)  string,

    static const char * const cdjpeg_message_table[] = {
#include "cderror.h"
        NULL
};


static boolean is_targa;
static boolean is_jpeg;
static boolean copy_markers;
// 静态方法
LOCAL(cjpeg_source_ptr)
select_file_type(j_compress_ptr cinfo, FILE *infile)
{
    int c;

    if (is_targa) {
#ifdef TARGA_SUPPORTED
        return jinit_read_targa(cinfo);
#else
        ERREXIT(cinfo, JERR_TGA_NOTCOMP);
#endif
    }

    if ((c = getc(infile)) == EOF)
        ERREXIT(cinfo, JERR_INPUT_EMPTY);
    if (ungetc(c, infile) == EOF)
        ERREXIT(cinfo, JERR_UNGETC_FAILED);

    switch (c) {

    case 'B':
        return jinit_read_bmp(cinfo, TRUE);

    case 'G':
        return jinit_read_gif(cinfo);

    case 'P':
        return jinit_read_ppm(cinfo);

    case 0x89:
        copy_markers = TRUE;
        return jinit_read_png(cinfo);

    case 0x00:
        return jinit_read_targa(cinfo);

    case 0xff:
        is_jpeg = TRUE;
        copy_markers = TRUE;
        return jinit_read_jpeg(cinfo);
    default:
        ERREXIT(cinfo, JERR_UNKNOWN_FORMAT);
        break;
    }

    return NULL;
}



static const char *progname;
static char *icc_filename;
static char *outfilename;
boolean memdst;
boolean report;
boolean strict;




LOCAL(void)
usage(void)

{
    fprintf(stderr, "usage: %s [switches] ", progname);
#ifdef TWO_FILE_COMMANDLINE
    fprintf(stderr, "inputfile outputfile\n");
#else
    fprintf(stderr, "[inputfile]\n");
#endif

    fprintf(stderr, "Switches (names may be abbreviated):\n");
    fprintf(stderr, "  -quality N[,...]   Compression quality (0..100; 5-95 is most useful range,\n");
    fprintf(stderr, "                     default is 75)\n");
    fprintf(stderr, "  -grayscale     Create monochrome JPEG file\n");
    fprintf(stderr, "  -rgb           Create RGB JPEG file\n");
#ifdef ENTROPY_OPT_SUPPORTED
    fprintf(stderr, "  -optimize      Optimize Huffman table (smaller file, but slow compression, enabled by default)\n");
#endif
#ifdef C_PROGRESSIVE_SUPPORTED
    fprintf(stderr, "  -progressive   Create progressive JPEG file (enabled by default)\n");
#endif
    fprintf(stderr, "  -baseline      Create baseline JPEG file (disable progressive coding)\n");
#ifdef TARGA_SUPPORTED
    fprintf(stderr, "  -targa         Input file is Targa format (usually not needed)\n");
#endif
    fprintf(stderr, "  -revert        Revert to standard defaults (instead of mozjpeg defaults)\n");
    fprintf(stderr, "  -fastcrush     Disable progressive scan optimization\n");
    fprintf(stderr, "  -dc-scan-opt   DC scan optimization mode\n");
    fprintf(stderr, "                 - 0 One scan for all components\n");
    fprintf(stderr, "                 - 1 One scan per component (default)\n");
    fprintf(stderr, "                 - 2 Optimize between one scan for all components and one scan for 1st component\n");
    fprintf(stderr, "                     plus one scan for remaining components\n");
    fprintf(stderr, "  -notrellis     Disable trellis optimization\n");
    fprintf(stderr, "  -trellis-dc    Enable trellis optimization of DC coefficients (default)\n");
    fprintf(stderr, "  -notrellis-dc  Disable trellis optimization of DC coefficients\n");
    fprintf(stderr, "  -tune-psnr     Tune trellis optimization for PSNR\n");
    fprintf(stderr, "  -tune-hvs-psnr Tune trellis optimization for PSNR-HVS (default)\n");
    fprintf(stderr, "  -tune-ssim     Tune trellis optimization for SSIM\n");
    fprintf(stderr, "  -tune-ms-ssim  Tune trellis optimization for MS-SSIM\n");
    fprintf(stderr, "Switches for advanced users:\n");
    fprintf(stderr, "  -noovershoot   Disable black-on-white deringing via overshoot\n");
    fprintf(stderr, "  -nojfif        Do not write JFIF (reduces size by 18 bytes but breaks standards; no known problems in Web browsers)\n");
#ifdef C_ARITH_CODING_SUPPORTED
    fprintf(stderr, "  -arithmetic    Use arithmetic coding\n");
#endif
#ifdef DCT_ISLOW_SUPPORTED
    fprintf(stderr, "  -dct int       Use accurate integer DCT method%s\n",
            (JDCT_DEFAULT == JDCT_ISLOW ? " (default)" : ""));
#endif
#ifdef DCT_IFAST_SUPPORTED
    fprintf(stderr, "  -dct fast      Use less accurate integer DCT method [legacy feature]%s\n",
            (JDCT_DEFAULT == JDCT_IFAST ? " (default)" : ""));
#endif
#ifdef DCT_FLOAT_SUPPORTED
    fprintf(stderr, "  -dct float     Use floating-point DCT method [legacy feature]%s\n",
            (JDCT_DEFAULT == JDCT_FLOAT ? " (default)" : ""));
#endif
    fprintf(stderr, "  -quant-baseline Use 8-bit quantization table entries for baseline JPEG compatibility\n");
    fprintf(stderr, "  -quant-table N Use predefined quantization table N:\n");
    fprintf(stderr, "                 - 0 JPEG Annex K\n");
    fprintf(stderr, "                 - 1 Flat\n");
    fprintf(stderr, "                 - 2 Tuned for MS-SSIM on Kodak image set\n");
    fprintf(stderr, "                 - 3 ImageMagick table by N. Robidoux (default)\n");
    fprintf(stderr, "                 - 4 Tuned for PSNR-HVS on Kodak image set\n");
    fprintf(stderr, "                 - 5 Table from paper by Klein, Silverstein and Carney\n");
    fprintf(stderr, "                 - 6 Table from paper by Watson, Taylor and Borthwick\n");
    fprintf(stderr, "                 - 7 Table from paper by Ahumada, Watson, Peterson\n");
    fprintf(stderr, "                 - 8 Table from paper by Peterson, Ahumada and Watson\n");
    fprintf(stderr, "  -icc FILE      Embed ICC profile contained in FILE\n");
    fprintf(stderr, "  -restart N     Set restart interval in rows, or in blocks with B\n");
#ifdef INPUT_SMOOTHING_SUPPORTED
    fprintf(stderr, "  -smooth N      Smooth dithered input (N=1..100 is strength)\n");
#endif
    fprintf(stderr, "  -maxmemory N   Maximum memory to use (in kbytes)\n");
    fprintf(stderr, "  -outfile name  Specify name for output file\n");
#if JPEG_LIB_VERSION >= 80 || defined(MEM_SRCDST_SUPPORTED)
    fprintf(stderr, "  -memdst        Compress to memory instead of file (useful for benchmarking)\n");
#endif
    fprintf(stderr, "  -report        Report compression progress\n");
    fprintf(stderr, "  -strict        Treat all warnings as fatal\n");
    fprintf(stderr, "  -verbose  or  -debug   Emit debug output\n");
    fprintf(stderr, "  -version       Print version information and exit\n");
    fprintf(stderr, "Switches for wizards:\n");
    fprintf(stderr, "  -qtables FILE  Use quantization tables given in FILE\n");
    fprintf(stderr, "  -qslots N[,...]    Set component quantization tables\n");
    fprintf(stderr, "  -sample HxV[,...]  Set component sampling factors\n");
#ifdef C_MULTISCAN_FILES_SUPPORTED
    fprintf(stderr, "  -scans FILE    Create multi-scan JPEG per script FILE\n");
#endif
    exit(EXIT_FAILURE);
}


LOCAL(int)
parse_switches(j_compress_ptr cinfo, int argc, char **argv,
               int last_file_arg_seen, boolean for_real)

{
    int argn;
    char *arg;
    boolean force_baseline;
    boolean simple_progressive;
    char *qualityarg = NULL;
    char *qtablefile = NULL;
    char *qslotsarg = NULL;
    char *samplearg = NULL;
    char *scansarg = NULL;



    force_baseline = FALSE;
#ifdef C_PROGRESSIVE_SUPPORTED
    simple_progressive = cinfo->num_scans == 0 ? FALSE : TRUE;
#else
    simple_progressive = FALSE;
#endif
    is_targa = FALSE;
    icc_filename = NULL;
    outfilename = NULL;
    memdst = FALSE;
    report = FALSE;
    strict = FALSE;
    cinfo->err->trace_level = 0;



    for (argn = 1; argn < argc; argn++) {
        arg = argv[argn];
        if (*arg != '-') {

            if (argn <= last_file_arg_seen) {
                outfilename = NULL;
                continue;
            }
            break;
        }
        arg++;

        if (keymatch(arg, "outfile", 4)) {

            if (++argn >= argc) {
                fprintf(stderr, "%s: missing argument for outfile\n", progname);
                usage();
            }
            outfilename = argv[argn];

        } else if (keymatch(arg, "progressive", 1)) {

#ifdef C_PROGRESSIVE_SUPPORTED
            simple_progressive = TRUE;

#else
            fprintf(stderr, "%s: sorry, progressive output was not compiled in\n",
                    progname);
            exit(EXIT_FAILURE);
#endif

        } else if (keymatch(arg, "memdst", 2)) {

#if JPEG_LIB_VERSION >= 80 || defined(MEM_SRCDST_SUPPORTED)
            memdst = TRUE;
#else
            fprintf(stderr, "%s: sorry, in-memory destination manager was not compiled in\n",
                    progname);
            exit(EXIT_FAILURE);
#endif

        } else if (keymatch(arg, "quality", 1)) {

            if (++argn >= argc) {
                fprintf(stderr, "%s: missing argument for quality\n", progname);
                usage();
            }
            qualityarg = argv[argn];

        }  else {
            fprintf(stderr, "%s: unknown option '%s'\n", progname, arg);
            usage();
        }
    }



    if (for_real) {



        if (qualityarg != NULL)
            if (! set_quality_ratings(cinfo, qualityarg, force_baseline)) {
                fprintf(stderr, "%s: can't set quality ratings\n", progname);
                usage();
            }

        if (qtablefile != NULL)
            if (! read_quant_tables(cinfo, qtablefile, force_baseline)) {
                fprintf(stderr, "%s: can't read qtable file\n", progname);
                usage();
            }

        if (qslotsarg != NULL)
            if (!set_quant_slots(cinfo, qslotsarg))
                usage();


        if (samplearg != NULL)
            if (! set_sample_factors(cinfo, samplearg)) {
                fprintf(stderr, "%s: can't set sample factors\n", progname);
                usage();
            }

#ifdef C_PROGRESSIVE_SUPPORTED
        if (simple_progressive)
            jpeg_simple_progression(cinfo);
#endif

#ifdef C_MULTISCAN_FILES_SUPPORTED
        if (scansarg != NULL)
            if (!read_scan_script(cinfo, scansarg))
                usage();
#endif
    }

    return argn;
}


METHODDEF(void)
my_emit_message(j_common_ptr cinfo, int msg_level)
{
    if (msg_level < 0) {

        cinfo->err->error_exit(cinfo);
    } else {
        if (cinfo->err->trace_level >= msg_level)
            cinfo->err->output_message(cinfo);
    }
}


cjpeg_img::cjpeg_img( )
{

}
int  compress(int argc, char **argv)
{

    struct jpeg_compress_struct cinfo;

    struct jpeg_error_mgr jerr;

    struct cdjpeg_progress_mgr progress;
    int file_index;
    cjpeg_source_ptr src_mgr;
    FILE *input_file = NULL;
    FILE *icc_file;
    JOCTET *icc_profile = NULL;
    long icc_len = 0;
    FILE *output_file = NULL;
    unsigned char *outbuffer = NULL;
    unsigned long outsize = 0;
    JDIMENSION num_scanlines;

    progname = argv[0];
    if (progname == NULL || progname[0] == 0)
        progname = "cjpeg";


    cinfo.err = jpeg_std_error(&jerr);
    jpeg_create_compress(&cinfo);

    jerr.addon_message_table = cdjpeg_message_table;
    jerr.first_addon_message = JMSG_FIRSTADDONCODE;
    jerr.last_addon_message = JMSG_LASTADDONCODE;



    cinfo.in_color_space = JCS_RGB;
    jpeg_set_defaults(&cinfo);



    file_index = parse_switches(&cinfo, argc, argv, 0, FALSE);

    if (strict)
        jerr.emit_message = my_emit_message;

#ifdef TWO_FILE_COMMANDLINE
    if (!memdst) {

        if (outfilename == NULL) {
            if (file_index != argc - 2) {
                fprintf(stderr, "%s: must name one input and one output file\n",
                        progname);
                usage();
            }
            outfilename = argv[file_index + 1];
        } else {
            if (file_index != argc - 1) {
                fprintf(stderr, "%s: must name one input and one output file\n",
                        progname);
                usage();
            }
        }
    }
#else

    if (file_index < argc - 1) {
        fprintf(stderr, "%s: only one input file\n", progname);
        usage();
    }
#endif


    if (file_index < argc) {
        if ((input_file = fopen(argv[file_index], READ_BINARY)) == NULL) {
            fprintf(stderr, "%s: can't open %s\n", progname, argv[file_index]);
            exit(EXIT_FAILURE);
        }
    } else {

        input_file = read_stdin();
    }


    if (outfilename != NULL) {
        if ((output_file = fopen(outfilename, WRITE_BINARY)) == NULL) {
            fprintf(stderr, "%s: can't open %s\n", progname, outfilename);
            exit(EXIT_FAILURE);
        }
    } else if (!memdst) {

        output_file = write_stdout();
    }

    if (icc_filename != NULL) {
        if ((icc_file = fopen(icc_filename, READ_BINARY)) == NULL) {
            fprintf(stderr, "%s: can't open %s\n", progname, icc_filename);
            exit(EXIT_FAILURE);
        }
        if (fseek(icc_file, 0, SEEK_END) < 0 ||
            (icc_len = ftell(icc_file)) < 1 ||
            fseek(icc_file, 0, SEEK_SET) < 0) {
            fprintf(stderr, "%s: can't determine size of %s\n", progname,
                    icc_filename);
            exit(EXIT_FAILURE);
        }
        if ((icc_profile = (JOCTET *)malloc(icc_len)) == NULL) {
            fprintf(stderr, "%s: can't allocate memory for ICC profile\n", progname);
            fclose(icc_file);
            exit(EXIT_FAILURE);
        }
        if (fread(icc_profile, icc_len, 1, icc_file) < 1) {
            fprintf(stderr, "%s: can't read ICC profile from %s\n", progname,
                    icc_filename);
            free(icc_profile);
            fclose(icc_file);
            exit(EXIT_FAILURE);
        }
        fclose(icc_file);
    }



    if (report) {
        start_progress_monitor((j_common_ptr)&cinfo, &progress);
        progress.report = report;
    }


    src_mgr = select_file_type(&cinfo, input_file);
    src_mgr->input_file = input_file;



    (*src_mgr->start_input) (&cinfo, src_mgr);


#if JPEG_RAW_READER
    if (!is_jpeg)
#endif
        jpeg_default_colorspace(&cinfo);


    file_index = parse_switches(&cinfo, argc, argv, 0, TRUE);


#if JPEG_LIB_VERSION >= 80 || defined(MEM_SRCDST_SUPPORTED)
    if (memdst)
        jpeg_mem_dest(&cinfo, &outbuffer, &outsize);
    else
#endif
        jpeg_stdio_dest(&cinfo, output_file);



    jpeg_start_compress(&cinfo, TRUE);


    if (copy_markers) {
        jpeg_saved_marker_ptr marker;


        for (marker = src_mgr->marker_list; marker != NULL; marker = marker->next) {
            if (cinfo.write_JFIF_header &&
                marker->marker == JPEG_APP0 &&
                marker->data_length >= 5 &&
                GETJOCTET(marker->data[0]) == 0x4A &&
                GETJOCTET(marker->data[1]) == 0x46 &&
                GETJOCTET(marker->data[2]) == 0x49 &&
                GETJOCTET(marker->data[3]) == 0x46 &&
                GETJOCTET(marker->data[4]) == 0)
                continue;
            if (cinfo.write_Adobe_marker &&
                marker->marker == JPEG_APP0+14 &&
                marker->data_length >= 5 &&
                GETJOCTET(marker->data[0]) == 0x41 &&
                GETJOCTET(marker->data[1]) == 0x64 &&
                GETJOCTET(marker->data[2]) == 0x6F &&
                GETJOCTET(marker->data[3]) == 0x62 &&
                GETJOCTET(marker->data[4]) == 0x65)
                continue;
            jpeg_write_marker(&cinfo, marker->marker, marker->data,
                              marker->data_length);
        }
    }
    if (icc_profile != NULL)
        jpeg_write_icc_profile(&cinfo, icc_profile, (unsigned int)icc_len);


    while (cinfo.next_scanline < cinfo.image_height) {
        num_scanlines = (*src_mgr->get_pixel_rows) (&cinfo, src_mgr);
#if JPEG_RAW_READER
        if (is_jpeg)
            (void) jpeg_write_raw_data(&cinfo, src_mgr->plane_pointer, num_scanlines);
        else
#endif
            (void) jpeg_write_scanlines(&cinfo, src_mgr->buffer, num_scanlines);
    }


    (*src_mgr->finish_input) (&cinfo, src_mgr);
    jpeg_finish_compress(&cinfo);
    jpeg_destroy_compress(&cinfo);


    if (input_file != stdin)
        fclose(input_file);
    if (output_file != stdout && output_file != NULL)
        fclose(output_file);

    if (report)
        end_progress_monitor((j_common_ptr)&cinfo);

    if (memdst) {

        free(outbuffer);
    }

    free(icc_profile);


    return (jerr.num_warnings ? EXIT_WARNING : EXIT_SUCCESS);
}
